#include <fstream>
#include <string>
#include <sstream>
#include <time.h>
#include <stdlib.h>
#include "ExprController.h"
#include "Runner\GARunner.h"
#include "Runner\PSORunner.h"
#include "Runner\GAGRunner.h"

using namespace std;


ExprController::ExprController(string const& cFile)
	:configFile(cFile)
{
	ct=new CoverageTable();
}

void ExprController::InitializeExperiment()
{
	ifstream in(configFile.c_str());
	string line;
	vector<double> tmp;
	getline(in,outFile);
	while(getline(in,line))                    ///////input all paramrters
	{
		if(line=="ga")                         ////algorithm
		{
			alg.push_back(0);
			getline(in,line);
			istringstream istream(line);
			int num;
			double po;
			istream>>num;             ///repeat num
			repNum.push_back(num);
			istream>>num;             ///evolution iteration number
			tmp.push_back(num);
			istream>>po;             ///crossover possibiliy
			tmp.push_back(po);
			istream>>po;             ///mutation possibiliy
			tmp.push_back(po);
			algPara.push_back(tmp);
		}
		else if(line=="pso")
		{
			alg.push_back(1);
			getline(in,line);
			istringstream istream(line);
			int num;
			double po;
			istream>>num;             ///repeat num
			repNum.push_back(num);
			istream>>num;             ///evolution iteration number
			tmp.push_back(num);
			istream>>po;             ///weight
			tmp.push_back(po);
			istream>>po;             ///c1
			tmp.push_back(po);
			istream>>po;             ///c2
			tmp.push_back(po);
			algPara.push_back(tmp);
		}
		else if(line=="gag")
		{
			alg.push_back(2);
			getline(in,line);
			istringstream istream(line);
			int num;
			double po;
			istream>>num;             ///repeat num
			repNum.push_back(num);
			istream>>num;             ///evolution iteration number
			tmp.push_back(num);
			istream>>po;             ///crossover possibiliy
			tmp.push_back(po);
			istream>>po;             ///mutation possibiliy
			tmp.push_back(po);
			istream>>po;             ///coverage percent
			tmp.push_back(po);
			algPara.push_back(tmp);
		}
		else if(line=="psog")
		{
			alg.push_back(1);
			getline(in,line);
			istringstream istream(line);
			int num;
			double po;
			istream>>num;             ///repeat num
			repNum.push_back(num);
			istream>>num;             ///evolution iteration number
			tmp.push_back(num);
			istream>>po;             ///weight
			tmp.push_back(po);
			istream>>po;             ///c1
			tmp.push_back(po);
			istream>>po;             ///c2
			tmp.push_back(po);
			istream>>po;             ///coverage percent
			tmp.push_back(po);
			algPara.push_back(tmp);
		}
		else
			cout<<"file para error!"<<endl;
		getline(in,line);
		istringstream istream(line);
		vector<int> pa;
		int base,power;
		while(istream>>base)                  ////factors
		{
			istream>>power;
			for(int i=0;i<power;i++)
				pa.push_back(base);
		}
		para.push_back(pa);
	}
	in.close();
}

void ExprController::InitializePop(int index)     //initialize test suite for GA or PSO runner
{
	srand(clock());
	int popsize=32;
	int caseSize=para[index].size();
	for(int i=0;i<popsize;i++)
	{
		vector<int> singleCase;
		for(int j=0;j<caseSize;j++)
			singleCase.push_back(rand()%para[index][j]);
		population.push_back(singleCase);
	}
}

bool ExprController::alreadyExist(int index)
{
	if(index==-1)
		return true;
	int suiteSize=suite.size();
	if(suiteSize==0)
		return false;
	for(int i=0;i<suiteSize;i++)
	{
		for(int j=0;j<suite[i].size();j++)
			if(population[index][j]!=suite[i][j])
				return false;
	}
	return true;
}

void ExprController::InitialTotalComb()
{
	int num;
	for(int i=0;i<para.size();i++)
	{
		num=0;
		for(int j=0;j<para[i].size()-1;j++)
		{
			for(int k=j+1;k<para[i].size();k++)
				num+=para[i][j]*para[i][j];
		}
		totalComb.push_back(num);
	}
}

int ExprController::Run()
{
	InitializeExperiment();
	InitialTotalComb();
	InitializeOut(outFile);
	int ExprNum=para.size();
	clock_t begin,finish;
	for(int i=0; i<ExprNum; ++i)
	{
		Out()<<"Running Experiment"<<i<<"...\n";
		Out()<<"Repeat number: "<<repNum[i]<<"\n\n";
		for(int j=0;j<repNum[i];j++)
		{
			cout<<"    repeat running "<<j<<"...\n";
			begin=clock();
			vector<int> tmp;
			suiteSize.push_back(tmp);
			suiteSize[i].push_back(RunOnce(i));
			finish=clock();
			suite.clear();
			consumeTime.push_back(tmp);
			consumeTime[i].push_back(finish-begin);
			cout<<"    end "<<j<<"\n";
			cout<<"    The number of test suite is: "<<suiteSize[i][j]<<"\n";
			cout<<"    Consumed time : " << consumeTime[i][j]<< "ms\n";          //CLOCKS_PER_SEC
		}
		Out()<<"End "<<i<<"\n";
		Summary(i);
	}
	return 0;
}

int ExprController::RunOnce(int index)
{
	if(alg[index]==0)
		runner=new GARunner(algPara[index][1],algPara[index][2],algPara[index][0],ct);
	else if(alg[index]==1)
		runner=new PSORunner(algPara[index][1],algPara[index][2],algPara[index][3],algPara[index][0],ct);
	else if(alg[index]==2)
		runner=new GAGRunner(algPara[index][1],algPara[index][2],algPara[index][0],algPara[index][3],ct);
	int caseIndex;
	ct->clean();         //clean the combination coverage table
//	cout<<"Remaining "<<totalComb[index]<<"\n";
	while(!Terminate(index))
	{
		InitializePop(index);
		caseIndex=runner->run(population, para[index]);
		if(!alreadyExist(caseIndex))
		{
			suite.push_back(population[caseIndex]);
			UpdateCovTable(caseIndex);
			///////for coverage//////////
//			int remainComb=RemainComb(index);
//			double percent=(double)(totalComb[index]-remainComb)/(double)totalComb[index];
//			Out()<<"Suite Size: "<<suite.size()<<"; Coverage percent: "<<percent<<"%\n";
//			cout<<"Remaining "<<remainComb<<"\n";
    		/////////////////////////////
		}
		population.clear();
	}
	return suite.size();
}

void ExprController::UpdateCovTable(int index)
{
	int size=population[index].size();
	for(int i=0;i<size-1;i++)
	{
		for(int j=i+1;j<size;j++)
		{
			int row=size*i-(i*(i+1)>>1)+j-i-1;
			int col=population[index][i]*11+population[index][j];
			ct->SetBit(row, col);                        //the max factor value is 11
		}
	}
}

void ExprController::Summary(int index)
{
	int size=repNum[index];
	int min=suiteSize[index][0];
	int totalcase=min,totaltime=consumeTime[index][0];
	double TimePerCase;
	for(int i=1;i<size;i++)
	{
		if(min>suiteSize[index][i])
			min=suiteSize[index][i];
		totalcase+=suiteSize[index][i];
		totaltime+=consumeTime[index][i];
	}
	TimePerCase=totaltime/totalcase;
	Out()<<"    The minimum suite size is: "<<min<<"\n";
	Out()<<"    Consumed Time on Every Case is: "<<TimePerCase<<"\n\n";
}

void ExprController::InitializeOut(std::string const& outStr)
{
	if( outStr == "console" )
	{
		pOut_ = &cout;
	}
	else
	{
		outFile_.open( outStr.c_str() );
		if(!outFile_)
			cout<<"open file error!"<<"\n";
		pOut_ = &outFile_;
	}
}

bool ExprController::Terminate(int paraIndex)
{
	int size=para[paraIndex].size();
	int ret=0;
	for(int i=0;i<size-1;i++)
	{
		for(int j=i+1;j<size;j++)
		{
			int row=size*i-(i*(i+1)>>1)+j-i-1;
			for(int k=0;k<para[paraIndex][i];k++)
			{
				for(int m=0;m<para[paraIndex][j];m++)
					if(!ct->CheckBit(row,k*11+m))
						return false;
			}
		}
	}
	//cout<<"remaining "<<ret<<" combinations!"<<"\n";
	return true;
}

int ExprController::RemainComb(int paraIndex)
{
	int size=para[paraIndex].size();
	int ret=0;
	for(int i=0;i<size-1;i++)
	{
		for(int j=i+1;j<size;j++)
		{
			int row=size*i-(i*(i+1)>>1)+j-i-1;
			for(int k=0;k<para[paraIndex][i];k++)
			{
				for(int m=0;m<para[paraIndex][j];m++)
					if(!ct->CheckBit(row,k*11+m))
						ret++;
			}
		}
	}
	//cout<<"remaining "<<ret<<" combinations!"<<"\n";
	return ret;
}

